﻿using System;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.Util;
using Bling.Physics;
using Bling.Animation;
using Bling.Properties;

namespace Bling.PhysicsTutorial {
  public static class ImmediateExtensions {
    public static PointBl CenterB(this Immediate.CustomBlock.Body body) {
      return body.GetProperty(body.Block.CenterBProperty);
    }
  }
  public class Immediate : PhysicsCanvas {
    public class CustomBlock : ImmediatePhysicsBlock<CustomBlock>, 
      ImmediateRender.Is<CustomBlock> {

      private static readonly GetProperty<CustomBlock, Point> ShakeProperty = "Shake".NewProperty<CustomBlock, Point>();
      internal readonly PhysicsProperty<Point> CenterBProperty;

      public PointBl Shake {
        get { return ShakeProperty[this]; }
        set { Shake.Bind = value; }
      }

      public readonly Random r = new Random();
      public readonly SweepList<int> Collisions;
      private readonly CanvasBl Owner;
      public CustomBlock(CanvasBl Owner, World World, int Count)
        : base(World, Count) {
        new ImmediateRender.Impl<CustomBlock>(this, Owner);
        this.Owner = Owner;
        Collisions = new SweepList<int>(World);
        CenterBProperty = this.NewBodyProperty<Point>("CenterB");
        this.ImmediateRenderE().Render.Bl().LeftTop.Bind = Shake * -1;
      }
      protected override void Init() {
        base.Init();
        ForAll(body => {
          body.Radius().Init = ((DoubleBl) r) * 10d;

          body.CenterX().Init = r * new PointBl(500,500);
          body.CenterX().Step = (nowV, oldV) => nowV.VerletStep(.05, oldV);
          body.CenterX().Old.Init = r * new PointBl(500, 500);

          var sz = new PointBl(2d, 2d) * body.Radius();
         // body.Center().Bind += new PointBl(0, 1);

          var center = body.CenterX();
          center.Old = center.Old.Restitution(center, sz + Shake, Owner.Size - sz + Shake);
          center.Bind = center.                 Clamp(sz + Shake, Owner.Size - sz + Shake);
        });
      }
      protected override void Activate() {
        base.Activate();
        WindowBl window;
        {
          object w = Owner.CurrentValue;
          while (!(w is Window)) w = ((FrameworkElement)w).Parent;
          window = (Window)w;
        }
        Shake.Bind = window.LeftTop;


        var SetColor = BrushBl.Assign<int, Brush>((i, c) => this[i].Brush(), (i, c) => c);
        for (int i = 0; i < Count; i++)
          SetColor(i, new SolidColorBrush() { Color = i.SelectColor() });

        Collisions.Init(DoubleBl.Func<int>(i => this[i].LeftTop().X), DoubleBl.Func<int>(i => this[i].RightBottom().X));

        var ComputeVec = PointBl.Func<int, int>((i0, i1) => {
          var diff = this[i1].CenterX() - this[i0].CenterX();
          var l = diff.Length;
          var diff0 = (l == 0).Condition(new PointBl(0, 1), new PointBl(diff.X / l, diff.Y / l));
          //diff0 = diff0.Check("NaN: 1=".Bl() + diff + " 0=" + diff0 + " L=" + l + " X=" + (diff.X / l) + " Y=" + (diff.Y / l));
          var total = this[i0].Radius() + this[i1].Radius();
          l = (total - l).Max(0);
          return diff0 * l * .5;
        });
        var Correct = PointBl.Assign<int, Point>((i, p) => this[i].CenterX(), (i, p) => this[i].CenterX() + p.Bl());
        Vector[] Corrections = new Vector[Count];
        Collisions.BeginCollide = () => {
          for (int i = 0; i < Count; i++) Corrections[i] = new Vector(0, 0);
        };
        var CheckY = BoolBl.Func<int, int>((i0, i1) => {
          var diff = this[i1].CenterX().Y - this[i0].CenterX().Y;
          //return diff.Abs <= 8d;
          return diff.Abs < this[i1].Radius() * 2d | diff.Abs < this[i0].Radius() * 2d;
        });
        Collisions.DoCollide = (i0, i1) => {
          if (CheckY(i0, i1)) {
            var diff = ComputeVec(i0, i1);
            //Chop(i1, diff);
            //Chop(i0, diff);
            Vector d1 = (Vector) diff;
            Vector d0 = d1 * -1d;
            Corrections[i1] += d1;
            Corrections[i0] += d0;
            //Correct(i1, diff);
            //Correct(i0, new Point(-diff.X, -diff.Y));
          }
          return;
        };
        Collisions.EndCollide = () => {
          for (int i = 0; i < Count; i++) Correct(i, (Point) Corrections[i]);
        };



        for (int i = 0; i < Count; i++) Collisions.Insert(i);

      }
    }
    static Immediate() {
    }

    public Immediate() : base("Immediate", 40) {
      Background = Brushes.LightGray;
    }

    protected override Action Init(CanvasBl MainCanvas, World World) {
      var block = new CustomBlock(MainCanvas, World, 1000);

      return () => { };
    }
  }

}
